--- title: 在 Debian 12 上完全手动安装 kubernetes v1.32.2 date: 2025-07-24 17:15:00 +0800 categories: [工具] tags: [kubernetes,k8s] pin: false --- ## 基础环境准备 ### 1.1 kubernetes集群组件概述 Kubernetes 集群由 Master 节点(控制节点)和 Node 节点(工作节点)组成,各自包含以下核心组件: #### 1.1.1 Master节点 1. **etcd** - 分布式键值存储数据库,保存集群的所有状态和配置数据(如 Pod、Service、Namespace 等)。 - 是 Kubernetes 的“唯一真实数据源”(Single Source of Truth)。 2. **API Server(kube-apiserver)** - 集群的入口,提供 REST API,处理所有操作请求(如创建、更新、删除资源)。 - 负责与其他组件通信(如 kubelet、kube-scheduler 等)。 3. **Scheduler(kube-scheduler)** - 监听未调度的 Pod,根据资源需求、节点负载等因素,将 Pod 分配到合适的 Node 上运行。 4. **Controller Manager(kube-controller-manager)** - 运行一系列控制器,确保集群状态与期望一致。 - 核心控制器包括: - Node Controller(监控节点状态) - Deployment Controller(管理副本数) - Service Controller(管理 Service 与 Endpoint) - 其他控制器(如 ReplicaSet、Namespace 控制器等)。 5. **(可选)Cloud Controller Manager** - 当集群运行在公有云环境时,负责与云平台交互(如负载均衡、存储卷、节点管理)。 - 解耦 Kubernetes 与特定云厂商的代码。 #### 1.1.2 Node 节点组件 1. **kubelet** - 运行在每个 Node 上的“节点代理”,负责: - 与 Master 通信,接收 Pod 定义(通过 API Server)。 - 管理 Pod 生命周期(启动、停止、监控容器)。 - 上报节点状态(如资源使用、Pod 状态)到 Master。 2. **kube-proxy** - 维护节点上的网络规则,实现 Service 的抽象(如负载均衡、服务发现)。 - 通过 iptables/IPVS 或用户空间代理转发流量到 Pod。 3. **容器运行时(Container Runtime)** - 负责运行容器的底层软件,如 Docker、containerd、CRI-O。 - 与 Kubernetes 通过 CRI(Container Runtime Interface)交互。 ### 1.2 集群主机规划 假设我们只有3台主机,为了兼顾硬件条件限制而搭建高可用集群,以下是较为合理的集群软硬件规划: | 节点名称 | IP | 操作系统 | 配置 | | -------- | --------------- | ------------------------------ | ------------------------------- | | k8s-101 | 192.168.122.101 | Debian GNU/Linux 12 (bookworm) | 内存:4G + SSD硬盘:30G + CPU:2核 | | k8s-102 | 192.168.122.102 | Debian GNU/Linux 12 (bookworm) | 内存:4G + SSD硬盘:30G + CPU:2核 | | k8s-103 | 192.168.122.103 | Debian GNU/Linux 12 (bookworm) | 内存:4G + SSD硬盘:30G + CPU:2核 | 这三台主机在集群中分别充当的角色如下: | 节点名称 | etcd服务器 | 控制节点 | 工作节点 | L4/L7代理 | 额外角色 | | -------- | ---------- | -------- | -------- | --------- | -------------------------- | | k8s-101 | ✓ | ✓ | ✕ | ✓ | 签发证书节点、主要控制节点 | | k8s-102 | ✓ | ✓ | ✓ | ✓ | | | k8s-103 | ✓ | ✓ | ✓ | ✕ | | 以上是在资源有限的情况下做的高可用资源分配,搭建集群的过程,其实就是把集群的各个软件组件合理安装到不同主机上,并且保证各个组件能正常工作。如果你的服务器资源充足,应当将组件独立部署到更多主机上,达到增强性能、可用性、可扩展性的目标,简化维护并增强安全性。 ### 1.3 设置hostsname 在 192.168.122.101 执行以下命令 ```bash hostnamectl set-hostname k8s-101 cat >> /etc/hosts <> /etc/hosts <> /etc/hosts <,在撰写文章时(2025.02.15)最新版本是 v2.0.2,我们将其安装到所有主机上,并作为容器运行时环境,安装步骤如下 ### 2.1 安装 containerd 从 下载 `containerd-<版本>-<操作系统>-<架构>.tar.gz` 存档,验证其 sha256sum,并将其解压到 `/usr/local` 目录下 ```shell # 下载并解压 tar Cxzvf /usr/local containerd-2.0.2-linux-amd64.tar.gz # 创建并配置 containerd.service mkdir -p /usr/local/lib/systemd/system/ cat > /usr/local/lib/systemd/system/containerd.service < 下载 `runc.<架构>` 二进制文件,验证其 `sha256sum`,并将其安装为 `/usr/local/sbin/runc`。 ```shell # 安装 install -m 755 runc.amd64 /usr/local/sbin/runc ``` 该二进制文件是静态构建的,应该适用于任何对应架构的 `Linux` 发行版。 ### 2.3 安装 CNI 插件 CNI(Container Network Interface) 插件用于配置容器的网络,包括分配 IP 地址、设置网络接口、配置路由等。从 下载 `cni-plugins-<操作系统>-<架构>-<版本>.tgz` 存档,验证其 `sha256sum`,并将其解压到 `/opt/cni/bin` 目录下,操作过程如下 ```shell mkdir -p /opt/cni/bin tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.6.2.tgz ``` 将 `/opt/cni/bin` 目录添加到 `$PATH` 中,执行以下命令追加到 `/etc/profile` 文件中 ```shell echo 'export PATH=$PATH:/opt/cni/bin' >> /etc/profile source /etc/profile ``` 创建 `CNI` 配置文件目录 ```shell mkdir -p /etc/cni/net.d cat > /etc/cni/net.d/10-mynet.conf < 下载对应的操作系统版本,在撰写这本文时 `nerdctl` 的版本是 `v2.0.3`,安装命令如下 ```shell wget https://github.com/containerd/nerdctl/releases/download/v2.0.3/nerdctl-2.0.3-linux-amd64.tar.gz tar -zxvf nerdctl-2.0.3-linux-amd64.tar.gz -C /usr/bin/ nerdctl ``` 最后,加载 `nerdctl` 的 `Bash` 自动补全功能,并设置 `containerd` 默认的名称空间为 `k8s.io`,如下 ```bash # 追加配置 cat >> /etc/profile < /etc/containerd/config.toml ``` 重启 containerd ```shell systemctl restart containerd ``` ## 签发SSL证书 ### 3.1 安装证书工具 `cfssl` 系列工具是 `Cloudflare` 提供的 `PKI/TLS` 工具,用于证书管理。可以在 找到对应的信息,在撰写文章时版本是 `1.6.6`,我们下载对应操作系统的版本,安装到 `k8s-101` 这台主机,以 linux amd64 为例安装命令如下 ```shell wget https://github.com/cloudflare/cfssl/releases/download/v1.6.5/cfssl_1.6.5_linux_amd64 -o /usr/local/bin/cfssl wget https://github.com/cloudflare/cfssl/releases/download/v1.6.5/cfssljson_1.6.5_linux_amd64 -o /usr/local/bin/cfssljson wget https://github.com/cloudflare/cfssl/releases/download/v1.6.5/cfssl-certinfo_1.6.5_linux_amd64 -o /usr/local/bin/cfssl-certinfo chmod a+x /usr/local/bin/cfssl* ``` 以下是它们的简要功能 | 工具 | 功能 | | -------------- | ---------------------------- | | cfssl | 核心工具,用于证书生成和管理 | | cfssl-json | 辅助工具,用于解析 JSON 输出 | | cfssl-certinfo | 用于查看证书详细信息 | ### 3.2 k8s所需证书概述 在 `Kubernetes` 集群中,我们需要为集群中的各个组件生成证书,以实现安全通信和身份验证。下图展示了 `Kubernetes` 所需的主要证书 ![k8s证书](/img/tools/k8s_pki.png) 我们将在 `k8s-101` 生成的各个证书存放到 `/etc/kubernetes/pki` 里,并同步到其他主机上。 ### 3.3 搭建CA `CA` 是证书的签发机构的简称,所有子证书的签发证书的前提是有一个签发机构,下文我们搭建自己的签发机构。 使用以下命令生成 `CA` 配置 ```shell mkdir -p /etc/kubernetes/pki cat > /etc/kubernetes/pki/ca-config.json < /etc/kubernetes/pki/ca-csr.json < /etc/kubernetes/pki/etcd-csr.json < /etc/kubernetes/pki/apiserver-csr.json < /etc/kubernetes/pki/controller-manager-csr.json < /etc/kubernetes/pki/scheduler-csr.json < /etc/kubernetes/pki/proxy-csr.json < /etc/kubernetes/pki/admin-csr.json << EOF { "CN": "admin", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "Guangzhou", "L": "Guangdong", "O": "system:masters", "OU": "system" } ] } EOF ``` 生成证书 ```shell cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=www admin-csr.json | cfssljson -bare admin ``` ### 3.5 同步证书 生成证书之后,将证书目录`/etc/kubernetes/pki`同步到其他主机。 ## 安装etcd 我们将使用 `k8s-101`、`k8s-102`、`k8s-103` 这三台主机搭建ectd集群。在撰写此文档时(2425.02.18),etcd最新稳定版本是 `3.5.18`,可以从 这个链接下载对应的安装包。 ### 4.1 etcd启动参数 常见参数说明说下 | 参数 | 对应环境变量 | 说明 | | ----------------------------- | -------------------------------- | ----------------------------------------------------------------------------------------- | | --name | ETCD_NAME | 当前etcd的唯一名称,要保证和其他节点不冲突 | | --data-dir | ETCD_DATA_DIR | 指定etcd存储数据的存储位置 | | --listen-peer-urls | ETCD_LISTEN_PEER_URLS | 端对端的通信url,包含主机地址和端口号,指定当前etcd和其他节点etcd通信时的服务地址和端口。 | | --listen-client-urls | ETCD_LISTEN_CLIENT_URLS | 指定当前etcd接收客户端指令的地址和端口,在这里的客户端我们指的是k8s集群的master节点 | | --initial-advertise-peer-urls | ETCD_INITIAL_ADVERTISE_PEER_URLS | 指定etcd广播端口,当前etcd会将数据同步到其他节点,通过2380端口发送 | | --advertise-client-urls | ETCD_ADVERTISE_CLIENT_URLS | 给客户端通告的端口 | | --initial-cluster | ETCD_INITIAL_CLUSTER | 定义etcd集群中所有节点的名称和IP,以及通信端口 | | --initial-cluster-token | ETCD_INITIAL_CLUSTER_TOKEN | 定义etcd中的token,所有节点的token必须保持一致 | | --initial-cluster-state | ETCD_INITIAL_CLUSTER_STATE | 定义etcd集群的状态,new代表新建集群,existing代表加入现有集群 | ### 4.2 创建数据目录 先创建etcd默认的配置文件目录和数据目录 ```bash mkdir -p /var/lib/etcd/ ``` 安装到`/opt`目录,后续的k8s集群组件我们将都安装在此 ```shell # 解压 tar -zxvf etcd-v3.5.18-linux-amd64.tar.gz # 将etc移到/opt目录,并修改etcd目录名 mv etcd-v3.5.18-linux-amd64/ /opt/etcd-v3.5.18 # 创建etcd软链接 ln -s /opt/etcd-v3.5.18 /opt/etcd ``` ### 4.3 创建etcd启动脚本 我们先在etcd目录编写启动脚本`/opt/etcd/startup.sh`,如下 ```shell #!/bin/bash ./etcd \ --name="etcd-server-101" \ --data-dir="/var/lib/etcd/" \ --listen-peer-urls="https://192.168.122.101:2380" \ --listen-client-urls="https://192.168.122.101:2379,http://127.0.0.1:2379" \ --initial-advertise-peer-urls="https://192.168.122.101:2380" \ --advertise-client-urls="https://192.168.122.101:2379" \ --initial-cluster="etcd-server-101=https://192.168.122.101:2380,etcd-server-102=https://192.168.122.102:2380,etcd-server-103=https://192.168.122.103:2380" \ --initial-cluster-token="etcd-cluster" \ --initial-cluster-state="new" \ --cert-file="/etc/kubernetes/pki/etcd.pem" \ --key-file="/etc/kubernetes/pki/etcd-key.pem" \ --trusted-ca-file="/etc/kubernetes/pki/ca.pem" \ --peer-cert-file="/etc/kubernetes/pki/etcd.pem" \ --peer-key-file="/etc/kubernetes/pki/etcd-key.pem" \ --peer-trusted-ca-file="/etc/kubernetes/pki/ca.pem" \ --peer-client-cert-auth \ --client-cert-auth ``` 给启动脚本添加权限 ```shell chmod +x /opt/etcd/startup.sh ``` ### 4.4 使用supervisor来启动etcd 现在我们要安装supervisor,用于管理etcd服务,后续的k8s相关组件,我们都用supervisor来管理 ```shell # 安装supervisor apt install supervisor -y # 启动supervisor systemctl start supervisor # 让superivisor开机自启 systemctl enable supervisor ``` 添加etcd的supervisor进程维护脚本`/etc/supervisor/conf.d/etcd-server.conf`,添加以下内容 ```ini [program:etcd-server-101] directory=/opt/etcd command=/opt/etcd/startup.sh numprocs=1 autostart=true autorestart=true startsecs=30 startretries=3 exitcodes=0,2 stopsignal=QUIT stopwaitsecs=10 user=root redirect_stderr=true stdout_logfile=/data/logs/supervisor/etcd.stdout.log stdout_logfile_maxbytes=64MB stdout_logfile_backups=4 stdout_capture_maxbytes=1MB stdout_event_enabled=false ``` > 注意:在不同的主机上使用不同的服务名称,这样好辨别,如k8s-101使用`etcd-server-101`,如k8s-102使用`etcd-server-102` supervisor相关参数: - `program`: 程序名称 - `directory`: 脚本目录 - `command`: 启动的命令 - `numprocs`: 启动的进程数 - `autostart`: 是否开启自动启动 - `autorestart`: 是否自动重启 - `startsecs`: 启动之后多少时间后判定为已起来 - `startretries`: 重启次数 - `exitcodes`: 退出的code - `stopsignal`: 停止信号 - `stopwaitsecs`: 停止等待的时间 - `redirect_stderr`: 是否重定向标准输出 - `stdout_logfile`: 进程标准输出内容写入文件 - `stdout_logfile_maxbytes`: stdout_logfile文件做log滚动时,单个stdout_logfile文件的最大字节数,默认50M,设置为0则认为不做log滚动方式 - `stdout_logfile_backups`: stdout_logfile备份文件个数,默认为10 - `stdout_capture_maxbytes`: 当进程处于stdout capture mode模式的时候,写入capture FIFO的最大字节数限制,默认为0,此时认为stdout capture mode模式关闭 - `stdout_event_enabled`: 如果设置为true,在进程写入标准文件是会发起PROCESS_LOG_STDOUT 更新supervisod配置文件 ```shell # 创建supervisor日志目录 mkdir -p /data/logs/supervisor/ # 更新supervisod配置 supervisorctl update ``` 通过`supervisorctl status`查询supervisord状态,看到如下内容,代表supervisor正常运行 ```shell root@debian:/opt/etcd# supervisorctl status etcd-server-101 RUNNING pid 85297, uptime 0:04:38 ``` 此时,我们再使用`netstat -luntp | grep etcd`查看网络服务端口,看到如下信息代表etcd已经正常启动 ```shell root@debian:/opt/etcd# netstat -luntp | grep etcd tcp 0 0 192.168.122.101:2379 0.0.0.0:* LISTEN 85298/./etcd tcp 0 0 127.0.0.1:2379 0.0.0.0:* LISTEN 85298/./etcd tcp 0 0 192.168.122.101:2380 0.0.0.0:* LISTEN 85298/./etcd ``` ### 4.5 集群验证 为了方便直接调用`etcdctl`命令,我们还可以创建其软连接 ```bash ln -s /opt/etcd/etcdctl /usr/local/bin/etcdctl ``` 我们在任意节点使用etcdctl命令检查集群状态,需要注意的是,要确切指定证书的位置 ```shell etcdctl --cacert="/etc/kubernetes/pki/ca.pem" --cert="/etc/kubernetes/pki/etcd.pem" --key="/etc/kubernetes/pki/etcd-key.pem" --endpoints="https://192.168.122.101:2379,https://192.168.122.102:2379,https://192.168.122.103:2379" endpoint status --write-out=table ``` 如果看到如下输出,代表 ectd 集群搭建成功 ```bash +------------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+ | ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS | +------------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+ | https://192.168.122.101:2379 | c8815bb4b21730b3 | 3.5.18 | 311 kB | true | false | 3 | 37628 | 37628 | | | https://192.168.122.102:2379 | f30299e8a0b43b4d | 3.5.18 | 311 kB | false | false | 3 | 37628 | 37628 | | | https://192.168.122.103:2379 | 61c90f737ccf2682 | 3.5.18 | 311 kB | false | false | 3 | 37628 | 37628 | | +------------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+ ``` 为了验证etcd集群是否正常工作,我们还可以现在`k8s-101`设置一个值,如下 ```bash etcdctl put name lixiaoming123 ``` 再通过`k8s-102`和`k8s-103`去读取值,如果正常取到,代表etcd集群正常工作,如下命令 ```bash etcdctl get name ``` 如果需要了解`etcdctl`这个指令的更多用法,使用`--help`参数即可查看。 ## 将kubernetes二进制安装包解压到系统中 在撰写这个文档时,kubernetes最新稳定版本为`v1.32.2`,所以这里也采用这个版本。通过 下载最新的对应操作系统的稳定版本。 我们下载好对应的 “Server Binarie” 之后,在所有k8s主机上执行安装,如下步骤 ```shell # 解压安装包 tar -zxvf kubernetes-server-linux-amd64.tar.gz # 将安装包移到/opt目录下并根据版本重命名 mv kubernetes /opt/kubernetes-v1.32.2 # 创建软连接 ln -s /opt/kubernetes-v1.32.2/ /opt/kubernetes ``` 在k8s二进制安装目录里包含了k8s源码包,还包含k8s核心组件的docker镜像,因为我们的核心服务不运行在容器里,所以可以删除掉,操作过程如下 ```shell # 进入k8s目录 cd /opt/kubernetes # 删除源代码 rm kubernetes-src.tar.gz # 删除二进制文件目录下以tar作为名称后缀的docker镜像包 rm -rf server/bin/*.tar ``` ## 安装apiserver 搭建好etcd数据库集群之后,我们就可以安装apiserver组件了,在所有主机上安装apiserver,以下是具体的安装过程。 ### 6.1 创建kubelet授权用户 因为后面要配置kubelet的bootstrap认证,即kubelet启动时自动创建CSR请求,这里需要在apiserver上开启token的认证。所以先在master上生成一个随机值作为token。下面在一台主机操作即可 ```shell # 创建证书目录 openssl rand -hex 10 ``` 假设生成的token为`88c916f382dc619a6bca`,把这个token写入到一个文件里,这里写入到 `/etc/kubernetes/bb.csv`,如下 ```bash cat > /etc/kubernetes/bb.csv < 19m v1.32.2 k8s-103 Ready 18m v1.32.2 ``` 我们还可以设置集群的标签 ```bash # 设置集群为node标签 kubectl label node k8s-102 node-role.kubernetes.io/node= kubectl label node k8s-103 node-role.kubernetes.io/node= ``` ## 安装proxy ### 12.1 创建kubeconfig配置文件 在`k8s-101`服务器上执行,如下命令 ```bash # 进入证书目录 cd /etc/kubernetes/pki/ # 创建集群信息 kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://192.168.122.100:7443 --kubeconfig=kube-proxy.kubeconfig # 创建用户信息 kubectl config set-credentials kube-proxy --client-certificate=proxy.pem --client-key=proxy-key.pem --embed-certs=true --kubeconfig=kube-proxy.kubeconfig # 创建上下文 kubectl config set-context default --cluster=kubernetes --user=kube-proxy --kubeconfig=kube-proxy.kubeconfig # 应用上下文 kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig # 移动到/etc/kubernetes/ mv kube-proxy.kubeconfig /etc/kubernetes/ ``` 创建完成后,同步到工作节点`192-debian`和`160-debian`。 ### 12.2 创建kube-proxy配置文件 在工作节点创建`/etc/kubernetes/kube-proxy.yaml`,内容如下 ```bash apiVersion: kubeproxy.config.k8s.io/v1alpha1 bindAddress: 0.0.0.0 clientConnection: kubeconfig: /etc/kubernetes/kube-proxy.kubeconfig clusterCIDR: 10.244.0.0/16 kind: KubeProxyConfiguration metricsBindAddress: 0.0.0.0:10249 mode: "ipvs" ``` ### 12.3 创建启动脚本 在两台主机执行以上脚本之后,我们创建`kube-proxy`的启动脚本文件`/opt/kubernetes/server/bin/kube-proxy.sh` ```shell #!/bin/bash ./kube-proxy \ --config=/etc/kubernetes/kube-proxy.yaml \ --v=2 ``` 添加可执行权限 ```shell chmod +x /opt/kubernetes/server/bin/kube-proxy.sh ``` ### 13.4 创建服务配置 创建supervisor的配置文件`/etc/supervisor/conf.d/kube-proxy.conf`文件,添加以下内容 ```ini [program:kube-proxy-102] directory=/opt/kubernetes/server/bin command=/opt/kubernetes/server/bin/kube-proxy.sh numprocs=1 autostart=true autorestart=true startsecs=30 startretries=3 exitcodes=0,2 stopsignal=QUIT stopwaitsecs=10 user=root redirect_stderr=true stdout_logfile=/data/logs/supervisor/kube-proxy.stdout.log stdout_logfile_maxbytes=64MB stdout_logfile_backups=4 stdout_capture_maxbytes=1MB stdout_event_enabled=false ``` 更新supervisor ```shell supervisorctl update ``` ### 12.5 创建权限配置文件 `kube-proxy` 是 Kubernetes 集群中的一个核心组件,负责在每个节点上维护网络规则,确保 Pod 之间的网络通信。为了实现这一功能,`kube-proxy` 需要与 Kubernetes API Server 进行交互,获取集群的网络信息(如 Service、Endpoint 等),并根据这些信息配置本地的网络规则(如 iptables 或 ipvs)。为了与 API Server 交互,`kube-proxy` 需要一定的权限,特别是访问节点资源的权限。 RBAC(基于角色的访问控制)是 Kubernetes 中用于管理权限的机制。通过创建 RBAC 配置,`kube-proxy` 被授予了以下权限:访问 nodes/proxy、nodes/stats、nodes/log 等资源,以便获取节点的网络和状态信息。执行相关操作(如 get、list、watch 等)来维护网络规则。如果没有这些权限,`kube-proxy` 将无法正常工作,导致集群中的网络功能失效。因此,启动 `kube-proxy` 之后创建 RBAC 配置是必要的。 在`k8s-101`上创建`/etc/kubernetes/rbac.yaml`,写入如下内容 ```yml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: annotations: rbac.authorization.kubernetes.io/autoupdate: "true" labels: kubernetes.io/bootstrapping: rbac-defaults name: system:kubernetes-to-kubelet rules: - apiGroups: - "" resources: - nodes/proxy - nodes/stats - nodes/log - nodes/spec - nodes/metrics verbs: - "*" --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: system:kubernetes namespace: "" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:kubernetes-to-kubelet subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: kubernetes ``` ### 12.6 创建角色及授权 ```bash kubectl apply -f /etc/kubernetes/rbac.yaml ``` ### 12.7 集群的验证 在两个节点都启动好kube-proxy服务之后,至此,集群的基本组件已经安装完成,下面我们来验证集群。在任意管理节点创建一个Pod类型的资源,添加`nginx-pod.yml`文件,添加以下内容 ```yaml apiVersion: v1 kind: Pod metadata: name: pod1 spec: containers: - name: nginx-pod image: nginx:alpine imagePullPolicy: IfNotPresent ports: - name: nginxport containerPort: 80 ``` 执行资源创建命令 ```shell kubectl create -f nginx-pod.yml ``` 使用以下命令验证pod是否正常运行 ```shell kubectl get pod -o wide ``` 如果返回如下内容,代表集群正常 ```shell NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod1 1/1 Running 0 94s 10.22.0.2 k8s-103 ``` 创建的pod运行在`k8s-103`这台主机上,在这台机使用`curl 10.22.0.2`命令能正常访问到nginx服务。但是如果我们在另一个节点`k8s-102`上执行`curl 10.22.0.2`会发现访问不到。原因是这两个节点上的容器在各自的虚拟网络内,我们将到后续的章节安装通过安装 k8s 网络插件的方式,实现不同工作节点的容器网络互相访问的功能。 ## 安装网络插件 以下的操作,我们在`k8s-101`节点去完成。 ### 13.1 安装calico Calico 是 Kubernetes 集群的网络基础设施,负责 Pod 的网络连接、跨节点通信和网络策略管理。根据以下步骤进行安装。 ```bash cd /etc/kubernetes wget https://docs.projectcalico.org/manifests/calico.yaml ``` 修改calico.yaml文件,将`CALICO_IPV4POOL_CIDR`改为和`kube-proxy`的配置一样,如下 ```ini - name: CALICO_IPV4POOL_CIDR value: "10.244.0.0/16" ``` 在calico配置文件中,定义了一下容器镜像,在运行calico的时候将会用到,可以使用 `cat calico.yaml | grep image` 命令查看所有需要的镜像列表,如下 ```yaml image: docker.io/calico/cni:v3.25.0 imagePullPolicy: IfNotPresent image: docker.io/calico/cni:v3.25.0 imagePullPolicy: IfNotPresent image: docker.io/calico/node:v3.25.0 imagePullPolicy: IfNotPresent image: docker.io/calico/node:v3.25.0 imagePullPolicy: IfNotPresent image: docker.io/calico/kube-controllers:v3.25.0 imagePullPolicy: IfNotPresent ``` 我们可以看到,这些镜像都来自`docker.io`,但因为一些原因,在撰写这篇文档时,国内访问 `docker.io` 的网络不太顺畅。因此你需要想办法让你的工作节点宿主机能拉取到这些镜像,最后再创建`calico`服务。或者你需要修改配置文件,改成这些镜像在可以拉取到的国内镜像站对应的镜像名。 解决依赖的镜像的拉取问题后,最后创建calico服务 ```bash kubectl apply -fcalico.yaml ``` 执行命令之后,calico会拉去远端的镜像并运行,执行 `kubectl get pods -n kube-system` 等到所有pod都处于`Running`状态代表服务启动完成,如下 ```bash NAME READY STATUS RESTARTS AGE calico-kube-controllers-6799f5f4b4-xqpf9 1/1 Running 0 3m34s calico-node-9bt29 1/1 Running 0 3m34s calico-node-djxvc 1/1 Running 0 3m34s ``` 此时calico已经正常运行了,如果上节创建的nginx的pod还没有删除的话,先删除掉再创建,如下命令 ```bash kubectl delete -f nginx-pod.yaml kubectl apply -f nginx-pod.yaml ``` 创建新的pod之后,使用`kubectl get pod -o wide`查看pod所处的节点,此时我们在任意工作节点请求该IP,都能成功请求。 ### 13.2 安装coredns CoreDNS 是 Kubernetes 集群的 DNS 服务,负责为集群内的服务提供域名解析和服务发现功能,使得 Pod 可以通过服务名称访问其他服务。它允许 Pod 通过服务名称(如 `my-service.default.svc.cluster.local`)来访问其他服务,而不需要知道具体的 IP 地址。 #### 13.2.1 下载基础资源配置文件 下载corndns资源配置文件 ```bash cd /etc/kubernetes wget https://github.com/kubernetes/kubernetes/blob/master/cluster/addons/dns/coredns/coredns.yaml.base -O coredns.yml ``` #### 13.2.2 修改配置 做出以下修改: 找到配置文件中的`__DNS__DOMAIN__`这两个变量改为集群域名,如下 ```yml kubernetes cluster.local in-addr.arpa ip6.arpa { fallthrough in-addr.arpa ip6.arpa } ``` 将 `__DNS__MEMORY__LIMIT_` 改为`512Mi`,如下 将`__DNS_SERVER__`改为kubelet配置文件中指定的集群IP地址`10.96.0.10`,如下 ```yml spec: selector: k8s-app: kube-dns clusterIP: 10.96.0.10 ``` #### 13.2.3 启动服务 使用以下命令启动服务 ```bash kubectl apply -f coredns.yml ``` ## 安装traefik-ingress ### 14.1 启动traefik服务 Traefik 在 Kubernetes (k8s) 中的作用主要是作为反向代理和负载均衡器,负责管理外部流量到集群内部服务的路由。我们先定义 traefik 的资源,在`/etc/kubernetes`目录下创建`traefik.yml`文件,添加以下内容 ```yaml apiVersion: v1 kind: ServiceAccount metadata: name: traefik-ingress-controller namespace: kube-system --- # 定义角色 apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: traefik-ingress-controller rules: - apiGroups: - "" resources: - services - endpoints - secrets verbs: - get - list - watch - apiGroups: - extensions - networking.k8s.io resources: - ingresses - ingressclasses verbs: - get - list - watch - apiGroups: - extensions - networking.k8s.io resources: - ingresses/status verbs: - update --- # 创建角色和账号绑定 apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: traefik-ingress-controller roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: traefik-ingress-controller subjects: - kind: ServiceAccount name: traefik-ingress-controller namespace: kube-system --- # 创建traefik服务 apiVersion: apps/v1 kind: Deployment metadata: name: traefik namespace: kube-system labels: app: traefik spec: replicas: 1 selector: matchLabels: app: traefik template: metadata: labels: app: traefik spec: serviceAccountName: traefik-ingress-controller containers: - name: traefik image: traefik:v2.10 ports: - name: web containerPort: 80 - name: websecure containerPort: 443 - name: admin containerPort: 8080 args: - --api.insecure=true - --providers.kubernetesingress - --entrypoints.web.Address=:80 - --entrypoints.websecure.Address=:443 --- # 创建traefik的service apiVersion: v1 kind: Service metadata: name: traefik namespace: kube-system spec: type: NodePort ports: - name: http port: 80 nodePort: 58180 - name: https port: 443 nodePort: 58181 - name: dashboard port: 8080 nodePort: 58182 selector: app: traefik ``` 基于traefik镜像启动的pod将创建运行三个端口服务,80和443对应ingress本身核心服务,我们后续可以将流量都转发到ingress的80端口,让ingress做流量调度。8080是traefik的控制面板后台服务。 我们定义了3个nodePort类型的service,目的是为了在每个工作节点上提供一个服务入口。后续再将请求负载到各个节点上。创建好配置文件后,执行以下命令启动服务 ```shell kubectl apply -f traefik.yml ``` 此时我们通过 `kubectl get svc -A -o wide | grep traefik` 命令可以看到如下结果 ```shell kube-system traefik NodePort 10.96.71.38 80:58180/TCP,443:58181/TCP,8080:58182/TCP 116s ``` 我们可以使用`ipvsadm -L`查看所有端口映射关系,现在我们在浏览器访问不同的工作节点上IP的`58180`端口,都会看到traefik的管理页面,代表安装成功。我们可以看到,集群节点端口与trafik端口的对应关系如下: | 集群节点端口 | Traefik端口 | | ------------ | ----------- | | 58180 | 80 | | 58181 | 443 | | 58182 | 8080 | 集群节点的端口由我们自定义,而traefik的8080端口是控制面板端口,80和443则是常见web服务的默认端口。 ### 14.2 配置负载均衡 我们在`k8s-101`、`k8s-102`节点上分别创建了nginx服务,现在我们将流量转发到这2个工作节点上,假设我们的业务服务器是`algs.tech`,在`nginx`服务器添加以下配置 ```ini upstream traefik_dashboard { server 192.168.122.102:58182 max_fails=3 fail_timeout=10s; server 192.168.122.103:58182 max_fails=3 fail_timeout=10s; } server { server_name traefik.algs.tech; location / { proxy_pass http://traefik_dashboard; proxy_set_header Host $http_host; proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; } } upstream traefik_http { server 192.168.122.102:58180 max_fails=3 fail_timeout=10s; server 192.168.122.103:58180 max_fails=3 fail_timeout=10s; } server { server_name *.algs.tech; listen 80; location / { proxy_pass http://traefik_http; proxy_set_header Host $http_host; proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; } } upstream traefik_https { server 192.168.122.102:58181 max_fails=3 fail_timeout=10s; server 192.168.122.103:58181 max_fails=3 fail_timeout=10s; } server { server_name *.algs.tech; listen 443 ssl; # ssl证书 ssl_certificate /etc/certs/ssl/algs.tech/fullchain.pem; ssl_certificate_key /etc/certs/ssl/algs.tech/key.pem; ssl_session_timeout 5m; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_session_cache shared:SSL:10m; ssl_prefer_server_ciphers on; location / { proxy_pass http://traefik_https; proxy_set_header Host $http_host; proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; } } ``` 在以上配置中,我们把`traefik.algs.tech`的请求转发到traefik的控制面板。同时,我们把`*.algs.tech`的所有80端口和443端口的流量转发到集群对应的traefik服务,由traefik来调度,后续发布服务我们只需要配置好 `ingress` 资源即可。 至此,集群的核心组件和核心插件已经全部安装完毕。 ## 部署web服务 在本节中,我们将部署一个简单的web服务,并通过`ingress`资源来发布到集群中。 ### 15.1 概述 部署k8s通常包含以下几个步骤 - 1.准备项目镜像,用于启动应用容器 - 2.通常创建Deployment资源的方式运行应用 - 3.创建应用的Service网络,用于关联Deployment - 4.创建对应的Ingress资源,用于调度7层流量 在下文,我们使用声明式的管理方式(通常指通过yaml配置文件来管理集群)来创建各个集群资源,完成应用部署。 ### 15.2 准备资源配置文件 我们通过部署 nginx 来演示一个应用在k8s部署的流程 #### 15.2.1 声明deployment 创建`whoami.yml`文件,添加以下内容 ```yaml kind: Deployment apiVersion: apps/v1 metadata: name: whoami labels: app: whoami spec: replicas: 1 selector: matchLabels: app: whoami template: metadata: labels: app: whoami spec: containers: - name: whoami image: nginx ports: - name: web containerPort: 80 ``` #### 15.2.2 声明service 创建`whoami-services.yml`文件,添加以下内容 ```yml apiVersion: v1 kind: Service metadata: name: whoami spec: ports: - name: web port: 80 targetPort: web selector: app: whoami ``` #### 15.2.3 声明ingress 创建`whoami-ingress.yml`,添加以下内容 ```yml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: whoami-ingress spec: rules: - host: nginx.algs.tech http: paths: - path: / pathType: Prefix backend: service: name: whoami port: name: web ``` ### 15.3 资源创建与验证 创建各个资源 ```bash kubectl apply -f whoami.yml \ -f whoami-services.yml \ -f whoami-ingress.yml ``` 执行以上命令之后,k8s将会拉取 nginx 的镜像,并按我们指定的配置去启动服务。 启动完成之后,我们在自己的桌面操作系统的电脑上将`nginx.algs.tech`域名解析到在前面通过 `keepalived` 创建的虚拟IP `192.168.122.100`,再使用浏览器访问域名,就顺利访问到了nginx的首页了。